package com.joker.aigc.tools.controller;

import cn.hutool.core.io.FileUtil;
import com.joker.aigc.domain.entity.R;
import com.joker.aigc.tools.domian.dto.VideoUploadRequestDTO;
import com.joker.aigc.tools.service.video.VideoService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@Slf4j
@RestController
@RequestMapping("/api/videos")
public class FileUploadController {

    public static final String TEMP_FILEPATH = System.getProperty("java.io.tmpdir") + File.separator;


    @Resource
    private VideoService videoService;

    /**
     * 上传视频
     *
     * @param thumbnailFile 缩略图文件
     * @param request       视频信息
     * @return 视频ID
     */
    @Operation(
            summary = "上传视频",
            description = "上传视频文件到MinIO存储，支持同时上传缩略图，并保存视频相关信息到数据库"
    )
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<Long> uploadVideo(String fileName, String fileMd5, MultipartFile thumbnailFile, VideoUploadRequestDTO request) {
        String filePath = Paths.get(TEMP_FILEPATH, fileMd5, fileName).toString();
        log.info("上传文件：{}", filePath);
        if (!Files.exists(Paths.get(filePath))) {
            throw new RuntimeException("文件不存在");
        }

        Long videoId = videoService.uploadVideo(Paths.get(TEMP_FILEPATH, fileMd5, fileName).toFile(), thumbnailFile, request);
        // 上传成功，删除临时文件
        FileUtil.del(filePath);
        return R.ok(videoId);
    }

    /**
     * 检查分片上传状态
     *
     * @param fileMd5
     * @param chunk
     * @param fileName
     * @return 2表示上传完毕， 1表示分片完成,0表示未上传，-1表示上传失败
     */
    @GetMapping("/check")
    public R<Integer> checkChunk(
            @RequestParam("fileMd5") String fileMd5,
            @RequestParam("chunk") int chunk,
            @RequestParam("fileName") String fileName) {

        // 检查文件是否已完整上传
        String filePath = Paths.get(TEMP_FILEPATH, fileMd5, fileName).toString();
        log.info("检查文件：{}", filePath);
        if (Files.exists(Paths.get(filePath))) {
            log.info("文件已完整上传：{}", filePath);
            return R.ok(1);
        }

        // 检查分片是否已上传
        String chunkPath = Paths.get(TEMP_FILEPATH, fileMd5, chunk + ".part").toString();
        if (Files.exists(Paths.get(chunkPath))) {
            log.info("分片已上传：{}", chunkPath);
            return R.ok(1);
        }
        log.info("分片未上传：{}", chunkPath);
        return R.ok(0);
    }

    /**
     * 上传分片
     *
     * @param file
     * @param fileMd5
     * @param chunk
     * @param chunkSize
     * @param totalChunks
     * @param fileName
     * @return 1表示成功，0表示失败
     * @throws IOException
     */
    @PostMapping("/chunk")
    public R<Integer> uploadChunk(
            @RequestParam("file") MultipartFile file,
            @RequestParam("fileMd5") String fileMd5,
            @RequestParam("chunk") int chunk,
            @RequestParam("chunkSize") int chunkSize,
            @RequestParam("totalChunks") int totalChunks,
            @RequestParam("fileName") String fileName) throws IOException {

        // 确保目录存在
        Path chunkDir = Paths.get(TEMP_FILEPATH, fileMd5);
        log.info("创建目录：{}", chunkDir);
        Files.createDirectories(chunkDir);

        // 保存分片
        String chunkFilename = chunk + ".part";
        Path chunkPath = chunkDir.resolve(chunkFilename);
        file.transferTo(chunkPath.toFile());

        // 如果是最后一个分片，合并文件
        if (chunk == totalChunks - 1) {
            mergeFiles(fileMd5, fileName, totalChunks);
        }
        return R.ok(1);
    }

    // 合并分片
    private void mergeFiles(String fileMd5, String fileName, int totalChunks) throws IOException {
        Path mergedFile = Paths.get(TEMP_FILEPATH, fileMd5, fileName);
        try (RandomAccessFile raf = new RandomAccessFile(mergedFile.toFile(), "rw")) {
            byte[] buffer = new byte[1024 * 1024]; // 1MB buffer

            for (int i = 0; i < totalChunks; i++) {
                Path chunkFile = Paths.get(TEMP_FILEPATH, fileMd5, i + ".part");
                try (RandomAccessFile chunkRaf = new RandomAccessFile(chunkFile.toFile(), "r")) {
                    int bytesRead;
                    while ((bytesRead = chunkRaf.read(buffer)) != -1) {
                        raf.write(buffer, 0, bytesRead);
                    }
                }
                // 删除分片文件
                Files.delete(chunkFile);
            }
        }
    }

    /**
     * 计算文件MD5（前端也可以计算）
     *
     * @param file 文件
     * @return MD5值
     * @throws IOException 读取文件时发生IO异常
     */
    @PostMapping("/md5")
    public R<String> calculateMd5(@RequestParam("file") MultipartFile file) throws IOException {
        String md5 = DigestUtils.md5Hex(file.getInputStream());
        return R.ok(md5);
    }
}